package chattle.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import chattle.shared.ChatMessage;
import chattle.shared.User;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SingleSelectionModel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Chattle implements EntryPoint {
	
	private final ChatServiceAsync chatService = GWT.create(ChatService.class);
	private final UserMgmtServiceAsync userMgmt = GWT.create(UserMgmtService.class);

	private User user = null;
	private List<User> on = new ArrayList<User>();
	private Map<String, ChatWindow> chatWindows = new HashMap<String, ChatWindow>(); 

	private final HTML loggedIn = new HTML();
	private final Button logInOut = new Button();
	private final CellList<User> online = new CellList<User>(new UserCell());

	public void onModuleLoad() {
		RootPanel root = RootPanel.get("chatContainer");
		initGui(root);
		
		userMgmt.getUser(new AsyncCallback<User>() {
			@Override
			public void onSuccess(User result) {
				setUser(result);
			}
		});
		
		Timer presenceUpdater = new Timer() {
			@Override
			public void run() {
				updatePresence();
			}};
		presenceUpdater.scheduleRepeating(5*1000);
		updatePresence();
		
		final Timer poller = new Timer() {
			@Override
			public void run() {
				poll();
			}};
		Timer delay = new Timer()
		{
			@Override
			public void run() {
				poller.scheduleRepeating(1*1000);
			}
		};
		delay.schedule(500);
	}
	
	private void initGui(RootPanel root) {
		VerticalPanel main = new VerticalPanel();
		
		HorizontalPanel userPanel = new HorizontalPanel();
		loggedIn.setVisible(false);
		userPanel.add(loggedIn);
		logInOut.setVisible(false);
		logInOut.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if(user == null)
					login();
				else
				{
					logout();
				}	
			}});
		userPanel.add(logInOut);
		main.add(userPanel);
		
		HorizontalPanel chatPanel = new HorizontalPanel();
		VerticalPanel onlinePanel = new VerticalPanel();
		onlinePanel.add(new Label("online:"));
		final SingleSelectionModel<User> selectionModel = new SingleSelectionModel<User>();
		online.setSelectionModel(selectionModel);
		selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
			public void onSelectionChange(SelectionChangeEvent event) {
				User user = selectionModel.getSelectedObject();
				openChatWindow(user);
			}
		});
		onlinePanel.add(online);
		chatPanel.add(onlinePanel);
		main.add(chatPanel);
		
		root.add(main);
		
		setUser(null);
	}
	
	private static final String openIdUrl = "www.google.com/accounts/o8/id";
	private static final AsyncCallback<String> redirectorCallback = new AsyncCallback<String>()
	{
		public void onSuccess(String url) {
			Window.open(url, "_self", "");
		}
	};
	private void login()
	{
		userMgmt.getLoginURL(openIdUrl, Window.Location.getHref(), redirectorCallback);
	}
	private void logout()
	{
		userMgmt.getLogoutURL(Window.Location.getHref(), redirectorCallback);
	}
	
	private ChatWindow openChatWindow(User partner)
	{
		ChatWindow cw = chatWindows.get(partner.getUserId());
		if(cw == null)
		{
			cw = new ChatWindow(partner, chatService);
			chatWindows.put(partner.getUserId(), cw);
		}
		cw.show();
		return cw;
	}
	
	protected void setUser(User user) {
		this.user = user;
		if (user == null) {
			loggedIn.setHTML("You are <b>not</b> logged in.");
			logInOut.setText("log in");
		}
		else
		{
			loggedIn.setHTML("You are logged in as <b>"+user+"</b>");
			logInOut.setText("log out");
		}
		loggedIn.setVisible(true);
		logInOut.setVisible(true);
	}

	private void updatePresence()
	{
		chatService.whosOnline(new AsyncCallback<List<User>>() {
			@Override
			public void onSuccess(List<User> result) {
				List<User> wentOff = Chattle.diff(on, result);
				wentOff(wentOff);
				List<User> wentOn = Chattle.diff(result, on);
				wentOn(wentOn);
				on = result;
				online.setRowData(on);
			}});
	}
	private void wentOn(List<User> wentOn)
	{
		
	}
	private void wentOff(List<User> wentOff)
	{
		
	}
	
	private void poll()
	{
		chatService.poll(new AsyncCallback<List<ChatMessage>>() {
			@Override
			public void onSuccess(List<ChatMessage> result) {
				if(result == null)
					return;
				for(ChatMessage m : result)
				{
					ChatWindow cw = openChatWindow(m.sender);
					cw.newMessage(m);
				}
			}});
	}
	
	public static <T> List<T> diff(List<T> a, List<T> b)
	{
		List<T> diff = new ArrayList<T>();
		for(T t : a)
			if(!b.contains(t))
				diff.add(t);
		return diff;
	}
	
	/**
	 * Escape an html string. Escaping data received from the client helps to prevent cross-site script vulnerabilities.
	 * 
	 * @param html
	 *            the html string to escape
	 * @return the escaped string
	 */
	@SuppressWarnings("unused")
	private static String escapeHtml(String html) {
		if (html == null) {
			return null;
		}
		return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
	}

}
